/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2009 Sun Microsystems, Inc.  Portions Copyrighted 2008 Richard Schilling.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s): Richard Schilling (coderroadie@gmail.com)
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.mail;

import java.io.*;
import java.lang.*;
import java.util.Vector;
import java.util.StringTokenizer;
import javax.mail.search.SearchTerm;
import javax.mail.event.*;

/**
 * Folder is an abstract class that represents a folder for mail
 * messages. Subclasses implement protocol specific Folders.
 * <p>
 * Folders can contain Messages, other Folders or both, thus providing a
 * tree-like hierarchy rooted at the Store's default folder. (Note that some
 * Folder implementations may not allow both Messages and other Folders in the
 * same Folder).
 * <p>
 * The interpretation of folder names is implementation dependent. The different
 * levels of hierarchy in a folder's full name are separated from each other by
 * the hierarchy delimiter character.
 * <p>
 * The case-insensitive full folder name (that is, the full name relative to the
 * default folder for a Store) <strong>INBOX</strong> is reserved to mean the
 * "primary folder for this user on this server". Not all Stores will provide an
 * INBOX folder, and not all users will have an INBOX folder at all times. The
 * name <strong>INBOX</strong> is reserved to refer to this folder, when it
 * exists, in Stores that provide it.
 * <p>
 * A Folder object obtained from a Store need not actually exist in the backend
 * store. The <code>exists</code> method tests whether the folder exists or not.
 * The <code>create</code> method creates a Folder.
 * <p>
 * A Folder is initially in the closed state. Certain methods are valid in this
 * state; the documentation for those methods note this. A Folder is opened by
 * calling its 'open' method. All Folder methods, except <code>open</code>,
 * <code>delete</code> and <code>renameTo</code>, are valid in this state.
 * <p>
 * The only way to get a Folder is by invoking the <code>getFolder</code> method
 * on Store, Folder, or Session, or by invoking the <code>list</code> or
 * <code>listSubscribed</code> methods on Folder. Folder objects returned by the
 * above methods are not cached by the Store. Thus, invoking the
 * <code>getFolder</code> method with the same folder name multiple times will
 * return distinct Folder objects. Likewise for the <code>list</code> and
 * <code>listSubscribed</code> methods.
 * <p>
 * The Message objects within the Folder are cached by the Folder. Thus,
 * invoking <code>getMessage(msgno)</code> on the same message number multiple
 * times will return the same Message object, until an expunge is done on this
 * Folder.
 * <p>
 * Message objects from a Folder are only valid while a Folder is open and
 * should not be accessed after the Folder is closed, even if the Folder is
 * subsequently reopened. Instead, new Message objects must be fetched from the
 * Folder after the Folder is reopened.
 * <p>
 * Note that a Message's message number can change within a session if the
 * containing Folder is expunged using the expunge method. Clients that use
 * message numbers as references to messages should be aware of this and should
 * be prepared to deal with situation (probably by flushing out existing message
 * number references and reloading them). Because of this complexity, it is
 * better for clients to use Message objects as references to messages, rather
 * than message numbers. Expunged Message objects still have to be pruned, but
 * other Message objects in that folder are not affected by the expunge.
 * 
 * @author John Mani
 * @author Bill Shannon
 */

public abstract class Folder {

    /**
     * The parent store.
     */
    protected Store store;

    /**
     * The open mode of this folder. The open mode is
     * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>,
     * or -1 if not known.
     * 
     * @since JavaMail 1.1
     */
    protected int mode = -1;

    /**
     * Constructor that takes a Store object.
     * 
     * @param store the Store that holds this folder
     */
    protected Folder(Store store) {
        this.store = store;
    }

    /**
     * Returns the name of this Folder.
     * <p>
     * This method can be invoked on a closed Folder.
     * 
     * @return name of the Folder
     */
    public abstract String getName();

    /**
     * Returns the full name of this Folder. If the folder resides under
     * the root hierarchy of this Store, the returned name is relative
     * to the root. Otherwise an absolute name, starting with the
     * hierarchy delimiter, is returned.
     * <p>
     * This method can be invoked on a closed Folder.
     * 
     * @return full name of the Folder
     */
    public abstract String getFullName();

    /**
     * Return a URLName representing this folder. The returned URLName
     * does <em>not</em> include the password used to access the store.
     * 
     * @return the URLName representing this folder
     * @see URLName
     * @since JavaMail 1.1
     */
    public URLName getURLName() throws MessagingException {
        URLName storeURL = getStore().getURLName();
        String fullname = getFullName();
        StringBuffer encodedName = new StringBuffer();

        if (fullname != null) {
            /*
             * // We need to encode each of the folder's names.
             * char separator = getSeparator();
             * StringTokenizer tok = new StringTokenizer(
             * fullname, new Character(separator).toString(), true);
             * 
             * while (tok.hasMoreTokens()) {
             * String s = tok.nextToken();
             * if (s.charAt(0) == separator)
             * encodedName.append(separator);
             * else
             * // XXX - should encode, but since there's no decoder...
             * //encodedName.append(java.net.URLEncoder.encode(s));
             * encodedName.append(s);
             * }
             */
            // append the whole thing, until we can encode
            encodedName.append(fullname);
        }

        /*
         * Sure would be convenient if URLName had a
         * constructor that took a base URLName.
         */
        return new URLName(storeURL.getProtocol(), storeURL.getHost(), storeURL.getPort(),
                encodedName.toString(), storeURL.getUsername(), null /*
                                                                      * no
                                                                      * password
                                                                      */);
    }

    /**
     * Returns the Store that owns this Folder object.
     * This method can be invoked on a closed Folder.
     * 
     * @return the Store
     */
    public Store getStore() {
        return store;
    }

    /**
     * Returns the parent folder of this folder.
     * This method can be invoked on a closed Folder. If this folder
     * is the top of a folder hierarchy, this method returns null.
     * <p>
     * Note that since Folder objects are not cached, invoking this method
     * returns a new distinct Folder object.
     * 
     * @return Parent folder
     */
    public abstract Folder getParent() throws MessagingException;

    /**
     * Tests if this folder physically exists on the Store.
     * This method can be invoked on a closed Folder.
     * 
     * @return true if the folder exists, otherwise false
     * @see #create
     * @exception MessagingException typically if the connection
     *                to the server is lost.
     */
    public abstract boolean exists() throws MessagingException;

    /**
     * Returns a list of Folders belonging to this Folder's namespace
     * that match the specified pattern. Patterns may contain the wildcard
     * characters <code>"%"</code>, which matches any character except hierarchy
     * delimiters, and <code>"*"</code>, which matches any character.
     * <p>
     * As an example, given the folder hierarchy:
     * 
     * <pre>
     *    Personal/
     *       Finance/
     *          Stocks
     *          Bonus
     *          StockOptions
     *       Jokes
     * </pre>
     * 
     * <code>list("*")</code> on "Personal" will return the whole hierarchy. <br>
     * <code>list("%")</code> on "Personal" will return "Finance" and "Jokes". <br>
     * <code>list("Jokes")</code> on "Personal" will return "Jokes".<br>
     * <code>list("Stock*")</code> on "Finance" will return "Stocks" and
     * "StockOptions".
     * <p>
     * Folder objects are not cached by the Store, so invoking this method on
     * the same pattern multiple times will return that many distinct Folder
     * objects.
     * <p>
     * This method can be invoked on a closed Folder.
     * 
     * @param pattern the match pattern
     * @return array of matching Folder objects. An empty
     *         array is returned if no matching Folders exist.
     * @see #listSubscribed
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public abstract Folder[] list(String pattern) throws MessagingException;

    /**
     * Returns a list of subscribed Folders belonging to this Folder's
     * namespace that match the specified pattern. If the folder does
     * not support subscription, this method should resolve to <code>list</code>
     * .
     * (The default implementation provided here, does just this).
     * The pattern can contain wildcards as for <code>list</code>.
     * <p>
     * Note that, at a given level of the folder hierarchy, a particular folder
     * may not be subscribed, but folders underneath that folder in the folder
     * hierarchy may be subscribed. In order to allow walking the folder
     * hierarchy, such unsubscribed folders may be returned, indicating that a
     * folder lower in the hierarchy is subscribed. The
     * <code>isSubscribed</code> method on a folder will tell whether any
     * particular folder is actually subscribed.
     * <p>
     * Folder objects are not cached by the Store, so invoking this method on
     * the same pattern multiple times will return that many distinct Folder
     * objects.
     * <p>
     * This method can be invoked on a closed Folder.
     * 
     * @param pattern the match pattern
     * @return array of matching subscribed Folder objects. An
     *         empty array is returned if no matching
     *         subscribed folders exist.
     * @see #list
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public Folder[] listSubscribed(String pattern) throws MessagingException {
        return list(pattern);
    }

    /**
     * Convenience method that returns the list of folders under this
     * Folder. This method just calls the <code>list(String pattern)</code>
     * method with <code>"%"</code> as the match pattern. This method can
     * be invoked on a closed Folder.
     * 
     * @return array of Folder objects under this Folder. An
     *         empty array is returned if no subfolders exist.
     * @see #list
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */

    public Folder[] list() throws MessagingException {
        return list("%");
    }

    /**
     * Convenience method that returns the list of subscribed folders
     * under this Folder. This method just calls the
     * <code>listSubscribed(String pattern)</code> method with <code>"%"</code>
     * as the match pattern. This method can be invoked on a closed Folder.
     * 
     * @return array of subscribed Folder objects under this
     *         Folder. An empty array is returned if no subscribed
     *         subfolders exist.
     * @see #listSubscribed
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public Folder[] listSubscribed() throws MessagingException {
        return listSubscribed("%");
    }

    /**
     * Return the delimiter character that separates this Folder's pathname
     * from the names of immediate subfolders. This method can be invoked
     * on a closed Folder.
     * 
     * @exception FolderNotFoundException if the implementation
     *                requires the folder to exist, but it does not
     * @return Hierarchy separator character
     */
    public abstract char getSeparator() throws MessagingException;

    /**
     * This folder can contain messages
     */
    public final static int HOLDS_MESSAGES = 0x01;

    /**
     * This folder can contain other folders
     */
    public final static int HOLDS_FOLDERS = 0x02;

    /**
     * Returns the type of this Folder, that is, whether this folder can hold
     * messages or subfolders or both. The returned value is an integer
     * bitfield with the appropriate bits set. This method can be invoked
     * on a closed folder.
     * 
     * @return integer with appropriate bits set
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @see #HOLDS_FOLDERS
     * @see #HOLDS_MESSAGES
     */
    public abstract int getType() throws MessagingException;

    /**
     * Create this folder on the Store. When this folder is created, any
     * folders in its path that do not exist are also created.
     * <p>
     * If the creation is successful, a CREATED FolderEvent is delivered to any
     * FolderListeners registered on this Folder and this Store.
     * 
     * @param type The type of this folder.
     * @return true if the creation succeeds, else false.
     * @exception MessagingException
     * @see #HOLDS_FOLDERS
     * @see #HOLDS_MESSAGES
     * @see javax.mail.event.FolderEvent
     */
    public abstract boolean create(int type) throws MessagingException;

    /**
     * Returns true if this Folder is subscribed.
     * <p>
     * This method can be invoked on a closed Folder.
     * <p>
     * The default implementation provided here just returns true.
     * 
     * @return true if this Folder is subscribed
     */
    public boolean isSubscribed() {
        return true;
    }

    /**
     * Subscribe or unsubscribe this Folder. Not all Stores support
     * subscription.
     * <p>
     * This method can be invoked on a closed Folder.
     * <p>
     * The implementation provided here just throws the
     * MethodNotSupportedException.
     * 
     * @param subscribe true to subscribe, false to unsubscribe
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MethodNotSupportedException if this store
     *                does not support subscription
     * @exception MessagingException
     */
    public void setSubscribed(boolean subscribe) throws MessagingException {
        throw new MethodNotSupportedException();
    }

    /**
     * Returns true if this Folder has new messages since the last time
     * this indication was reset. When this indication is set or reset
     * depends on the Folder implementation (and in the case of IMAP,
     * depends on the server). This method can be used to implement
     * a lightweight "check for new mail" operation on a Folder without
     * opening it. (For example, a thread that monitors a mailbox and
     * flags when it has new mail.) This method should indicate whether
     * any messages in the Folder have the <code>RECENT</code> flag set.
     * <p>
     * Note that this is not an incremental check for new mail, i.e., it cannot
     * be used to determine whether any new messages have arrived since the last
     * time this method was invoked. To implement incremental checks, the Folder
     * needs to be opened.
     * <p>
     * This method can be invoked on a closed Folder that can contain Messages.
     * 
     * @return true if the Store has new Messages
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public abstract boolean hasNewMessages() throws MessagingException;

    /**
     * Return the Folder object corresponding to the given name. Note that
     * this folder does not physically have to exist in the Store. The
     * <code>exists()</code> method on a Folder indicates whether it really
     * exists on the Store.
     * <p>
     * In some Stores, name can be an absolute path if it starts with the
     * hierarchy delimiter. Otherwise, it is interpreted relative to this
     * Folder.
     * <p>
     * Folder objects are not cached by the Store, so invoking this method on
     * the same name multiple times will return that many distinct Folder
     * objects.
     * <p>
     * This method can be invoked on a closed Folder.
     * 
     * @param name name of the Folder
     * @return Folder object
     * @exception MessagingException
     */
    public abstract Folder getFolder(String name) throws MessagingException;

    /**
     * Delete this Folder. This method will succeed only on a closed
     * Folder.
     * <p>
     * The <code>recurse</code> flag controls whether the deletion affects
     * subfolders or not. If true, all subfolders are deleted, then this folder
     * itself is deleted. If false, the behaviour is dependent on the folder
     * type and is elaborated below:
     * <p>
     * <ul>
     * <li>The folder can contain only messages: (type == HOLDS_MESSAGES). <br>
     * All messages within the folder are removed. The folder itself is then
     * removed. An appropriate FolderEvent is generated by the Store and this
     * folder.
     * <p>
     * <li>The folder can contain only subfolders: (type == HOLDS_FOLDERS). <br>
     * If this folder is empty (does not contain any subfolders at all), it is
     * removed. An appropriate FolderEvent is generated by the Store and this
     * folder.<br>
     * If this folder contains any subfolders, the delete fails and returns
     * false.
     * <p>
     * <li>The folder can contain subfolders as well as messages: <br>
     * If the folder is empty (no messages or subfolders), it is removed. If the
     * folder contains no subfolders, but only messages, then all messages are
     * removed. The folder itself is then removed. In both the above cases, an
     * appropriate FolderEvent is generated by the Store and this folder.
     * <p>
     * If the folder contains subfolders there are 3 possible choices an
     * implementation is free to do:
     * <p>
     * <ol>
     * <li>The operation fails, irrespective of whether this folder contains
     * messages or not. Some implementations might elect to go with this simple
     * approach. The delete() method returns false.
     * <li>Any messages within the folder are removed. Subfolders are not
     * removed. The folder itself is not removed or affected in any manner. The
     * delete() method returns true. And the exists() method on this folder will
     * return true indicating that this folder still exists. <br>
     * An appropriate FolderEvent is generated by the Store and this folder.
     * <li>Any messages within the folder are removed. Subfolders are not
     * removed. The folder itself changes its type from HOLDS_FOLDERS |
     * HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new messages cannot be added to
     * this folder, but new subfolders can be created underneath. The delete()
     * method returns true indicating success. The exists() method on this
     * folder will return true indicating that this folder still exists. <br>
     * An appropriate FolderEvent is generated by the Store and this folder.
     * </ol>
     * </ul>
     * 
     * @return true if the Folder is deleted successfully
     * @exception FolderNotFoundException if this folder does
     *                not exist
     * @exception IllegalStateException if this folder is not in
     *                the closed state.
     * @exception MessagingException
     * @see javax.mail.event.FolderEvent
     */
    public abstract boolean delete(boolean recurse) throws MessagingException;

    /**
     * Rename this Folder. This method will succeed only on a closed
     * Folder.
     * <p>
     * If the rename is successful, a RENAMED FolderEvent is delivered to
     * FolderListeners registered on this folder and its containing Store.
     * 
     * @param f a folder representing the new name for this Folder
     * @return true if the Folder is renamed successfully
     * @exception FolderNotFoundException if this folder does
     *                not exist
     * @exception IllegalStateException if this folder is not in
     *                the closed state.
     * @exception MessagingException
     * @see javax.mail.event.FolderEvent
     */
    public abstract boolean renameTo(Folder f) throws MessagingException;

    /**
     * The Folder is read only. The state and contents of this
     * folder cannot be modified.
     */
    public static final int READ_ONLY = 1;

    /**
     * The state and contents of this folder can be modified.
     */
    public static final int READ_WRITE = 2;

    /**
     * Open this Folder. This method is valid only on Folders that
     * can contain Messages and that are closed.
     * <p>
     * If this folder is opened successfully, an OPENED ConnectionEvent is
     * delivered to any ConnectionListeners registered on this Folder.
     * <p>
     * The effect of opening multiple connections to the same folder on a
     * specifc Store is implementation dependent. Some implementations allow
     * multiple readers, but only one writer. Others allow multiple writers as
     * well as readers.
     * 
     * @param mode open the Folder READ_ONLY or READ_WRITE
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not in
     *                the closed state.
     * @exception MessagingException
     * @see #READ_ONLY
     * @see #READ_WRITE
     * @see #getType()
     * @see javax.mail.event.ConnectionEvent
     */
    public abstract void open(int mode) throws MessagingException;

    /**
     * Close this Folder. This method is valid only on open Folders.
     * <p>
     * A CLOSED ConnectionEvent is delivered to any ConnectionListeners
     * registered on this Folder. Note that the folder is closed even if this
     * method terminates abnormally by throwing a MessagingException.
     * 
     * @param expunge expunges all deleted messages if this flag is true
     * @exception IllegalStateException if this folder is not opened
     * @exception MessagingException
     * @see javax.mail.event.ConnectionEvent
     */
    public abstract void close(boolean expunge) throws MessagingException;

    /**
     * Indicates whether this Folder is in the 'open' state.
     * 
     * @return true if this Folder is in the 'open' state.
     */
    public abstract boolean isOpen();

    /**
     * Return the open mode of this folder. Returns
     * <code>Folder.READ_ONLY</code>, <code>Folder.READ_WRITE</code>,
     * or -1 if the open mode is not known (usually only because an older
     * <code>Folder</code> provider has not been updated to use this new
     * method).
     * 
     * @exception IllegalStateException if this folder is not opened
     * @return the open mode of this folder
     * @since JavaMail 1.1
     */
    public int getMode() {
        if (!isOpen())
            throw new IllegalStateException("Folder not open");
        return mode;
    }

    /**
     * Get the permanent flags supported by this Folder. Returns a Flags
     * object that contains all the flags supported.
     * <p>
     * The special flag <code>Flags.USER </code> indicates that this Folder
     * supports arbitrary user-defined flags.
     * <p>
     * The supported permanent flags for a folder may not be available until the
     * folder is opened.
     * 
     * @return permanent flags, or null if not known
     */
    public abstract Flags getPermanentFlags();

    /**
     * Get total number of messages in this Folder.
     * <p>
     * This method can be invoked on a closed folder. However, note that for
     * some folder implementations, getting the total message count can be an
     * expensive operation involving actually opening the folder. In such cases,
     * a provider can choose not to support this functionality in the closed
     * state, in which case this method must return -1.
     * <p>
     * Clients invoking this method on a closed folder must be aware that this
     * is a potentially expensive operation. Clients must also be prepared to
     * handle a return value of -1 in this case.
     * 
     * @return total number of messages. -1 may be returned
     *         by certain implementations if this method is
     *         invoked on a closed folder.
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public abstract int getMessageCount() throws MessagingException;

    /**
     * Get the number of new messages in this Folder.
     * <p>
     * This method can be invoked on a closed folder. However, note that for
     * some folder implementations, getting the new message count can be an
     * expensive operation involving actually opening the folder. In such cases,
     * a provider can choose not to support this functionality in the closed
     * state, in which case this method must return -1.
     * <p>
     * Clients invoking this method on a closed folder must be aware that this
     * is a potentially expensive operation. Clients must also be prepared to
     * handle a return value of -1 in this case.
     * <p>
     * This implementation returns -1 if this folder is closed. Else this
     * implementation gets each Message in the folder using
     * <code>getMessage(int)</code> and checks whether its <code>RECENT</code>
     * flag is set. The total number of messages that have this flag set is
     * returned.
     * 
     * @return number of new messages. -1 may be returned
     *         by certain implementations if this method is
     *         invoked on a closed folder.
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public synchronized int getNewMessageCount() throws MessagingException {
        if (!isOpen())
            return -1;

        int newmsgs = 0;
        int total = getMessageCount();
        for (int i = 1; i <= total; i++) {
            try {
                if (getMessage(i).isSet(Flags.Flag.RECENT))
                    newmsgs++;
            } catch (MessageRemovedException me) {
                // This is an expunged message, ignore it.
                continue;
            }
        }
        return newmsgs;
    }

    /**
     * Get the total number of unread messages in this Folder.
     * <p>
     * This method can be invoked on a closed folder. However, note that for
     * some folder implementations, getting the unread message count can be an
     * expensive operation involving actually opening the folder. In such cases,
     * a provider can choose not to support this functionality in the closed
     * state, in which case this method must return -1.
     * <p>
     * Clients invoking this method on a closed folder must be aware that this
     * is a potentially expensive operation. Clients must also be prepared to
     * handle a return value of -1 in this case.
     * <p>
     * This implementation returns -1 if this folder is closed. Else this
     * implementation gets each Message in the folder using
     * <code>getMessage(int)</code> and checks whether its <code>SEEN</code>
     * flag is set. The total number of messages that do not have this flag set
     * is returned.
     * 
     * @return total number of unread messages. -1 may be returned
     *         by certain implementations if this method is
     *         invoked on a closed folder.
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     */
    public synchronized int getUnreadMessageCount() throws MessagingException {
        if (!isOpen())
            return -1;

        int unread = 0;
        int total = getMessageCount();
        for (int i = 1; i <= total; i++) {
            try {
                if (!getMessage(i).isSet(Flags.Flag.SEEN))
                    unread++;
            } catch (MessageRemovedException me) {
                // This is an expunged message, ignore it.
                continue;
            }
        }
        return unread;
    }

    /**
     * Get the number of deleted messages in this Folder.
     * <p>
     * This method can be invoked on a closed folder. However, note that for
     * some folder implementations, getting the deleted message count can be an
     * expensive operation involving actually opening the folder. In such cases,
     * a provider can choose not to support this functionality in the closed
     * state, in which case this method must return -1.
     * <p>
     * Clients invoking this method on a closed folder must be aware that this
     * is a potentially expensive operation. Clients must also be prepared to
     * handle a return value of -1 in this case.
     * <p>
     * This implementation returns -1 if this folder is closed. Else this
     * implementation gets each Message in the folder using
     * <code>getMessage(int)</code> and checks whether its <code>DELETED</code>
     * flag is set. The total number of messages that have this flag set is
     * returned.
     * 
     * @return number of deleted messages. -1 may be returned
     *         by certain implementations if this method is
     *         invoked on a closed folder.
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException
     * @since JavaMail 1.3
     */
    public synchronized int getDeletedMessageCount() throws MessagingException {
        if (!isOpen())
            return -1;

        int deleted = 0;
        int total = getMessageCount();
        for (int i = 1; i <= total; i++) {
            try {
                if (getMessage(i).isSet(Flags.Flag.DELETED))
                    deleted++;
            } catch (MessageRemovedException me) {
                // This is an expunged message, ignore it.
                continue;
            }
        }
        return deleted;
    }

    /**
     * Get the Message object corresponding to the given message
     * number. A Message object's message number is the relative
     * position of this Message in its Folder. Messages are numbered
     * starting at 1 through the total number of message in the folder.
     * Note that the message number for a particular Message can change
     * during a session if other messages in the Folder are deleted and
     * the Folder is expunged.
     * <p>
     * Message objects are light-weight references to the actual message that
     * get filled up on demand. Hence Folder implementations are expected to
     * provide light-weight Message objects.
     * <p>
     * Unlike Folder objects, repeated calls to getMessage with the same message
     * number will return the same Message object, as long as no messages in
     * this folder have been expunged.
     * <p>
     * Since message numbers can change within a session if the folder is
     * expunged , clients are advised not to use message numbers as references
     * to messages. Use Message objects instead.
     * 
     * @param msgnum the message number
     * @return the Message object
     * @see #getMessageCount
     * @see #fetch
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not opened
     * @exception IndexOutOfBoundsException if the message number
     *                is out of range.
     * @exception MessagingException
     */
    public abstract Message getMessage(int msgnum) throws MessagingException;

    /**
     * Get the Message objects for message numbers ranging from start
     * through end, both start and end inclusive. Note that message
     * numbers start at 1, not 0.
     * <p>
     * Message objects are light-weight references to the actual message that
     * get filled up on demand. Hence Folder implementations are expected to
     * provide light-weight Message objects.
     * <p>
     * This implementation uses getMessage(index) to obtain the required Message
     * objects. Note that the returned array must contain
     * <code>(end-start+1)</code> Message objects.
     * 
     * @param start the number of the first message
     * @param end the number of the last message
     * @return the Message objects
     * @see #fetch
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not opened.
     * @exception IndexOutOfBoundsException if the start or end
     *                message numbers are out of range.
     * @exception MessagingException
     */
    public synchronized Message[] getMessages(int start, int end) throws MessagingException {
        Message[] msgs = new Message[end - start + 1];
        for (int i = start; i <= end; i++)
            msgs[i - start] = getMessage(i);
        return msgs;
    }

    /**
     * Get the Message objects for message numbers specified in
     * the array.
     * <p>
     * Message objects are light-weight references to the actual message that
     * get filled up on demand. Hence Folder implementations are expected to
     * provide light-weight Message objects.
     * <p>
     * This implementation uses getMessage(index) to obtain the required Message
     * objects. Note that the returned array must contain
     * <code>msgnums.length</code> Message objects
     * 
     * @param msgnums the array of message numbers
     * @return the array of Message objects.
     * @see #fetch
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not opened.
     * @exception IndexOutOfBoundsException if any message number
     *                in the given array is out of range.
     * @exception MessagingException
     */
    public synchronized Message[] getMessages(int[] msgnums) throws MessagingException {
        int len = msgnums.length;
        Message[] msgs = new Message[len];
        for (int i = 0; i < len; i++)
            msgs[i] = getMessage(msgnums[i]);
        return msgs;
    }

    /**
     * Get all Message objects from this Folder. Returns an empty array
     * if the folder is empty.
     * Clients can use Message objects (instead of sequence numbers)
     * as references to the messages within a folder; this method supplies
     * the Message objects to the client. Folder implementations are
     * expected to provide light-weight Message objects, which get
     * filled on demand.
     * <p>
     * This implementation invokes <code>getMessageCount()</code> to get the
     * current message count and then uses <code>getMessage()</code> to get
     * Message objects from 1 till the message count.
     * 
     * @return array of Message objects, empty array if folder
     *         is empty.
     * @see #fetch
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not opened.
     * @exception MessagingException
     */
    public synchronized Message[] getMessages() throws MessagingException {
        if (!isOpen())	// otherwise getMessageCount might return -1
            throw new IllegalStateException("Folder not open");
        int total = getMessageCount();
        Message[] msgs = new Message[total];
        for (int i = 1; i <= total; i++)
            msgs[i - 1] = getMessage(i);
        return msgs;
    }

    /**
     * Append given Messages to this folder. This method can be
     * invoked on a closed Folder. An appropriate MessageCountEvent
     * is delivered to any MessageCountListener registered on this
     * folder when the messages arrive in the folder.
     * <p>
     * Folder implementations must not abort this operation if a Message in the
     * given message array turns out to be an expunged Message.
     * 
     * @param msgs array of Messages to be appended
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception MessagingException if the append failed.
     */
    public abstract void appendMessages(Message[] msgs) throws MessagingException;

    /**
     * Prefetch the items specified in the FetchProfile for the
     * given Messages.
     * <p>
     * Clients use this method to indicate that the specified items are needed
     * en-masse for the given message range. Implementations are expected to
     * retrieve these items for the given message range in a efficient manner.
     * Note that this method is just a hint to the implementation to prefetch
     * the desired items.
     * <p>
     * An example is a client filling its header-view window with the Subject,
     * From and X-mailer headers for all messages in the folder.
     * <p>
     * <blockquote>
     * 
     * <pre>
     * 
     * Message[] msgs = folder.getMessages();
     * 
     * FetchProfile fp = new FetchProfile();
     * fp.add(FetchProfile.Item.ENVELOPE);
     * fp.add(&quot;X-mailer&quot;);
     * folder.fetch(msgs, fp);
     * 
     * for (int i = 0; i &lt; folder.getMessageCount(); i++) {
     *     display(msg[i].getFrom());
     *     display(msg[i].getSubject());
     *     display(msg[i].getHeader(&quot;X-mailer&quot;));
     * }
     * 
     * </pre>
     * 
     * </blockquote>
     * <p>
     * The implementation provided here just returns without doing anything
     * useful. Providers wanting to provide a real implementation for this
     * method should override this method.
     * 
     * @param msgs fetch items for these messages
     * @param fp the FetchProfile
     * @exception IllegalStateException if this folder is not opened
     * @exception MessagingException.
     */
    public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
        return;
    }

    /**
     * Set the specified flags on the messages specified in the array.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder.
     * <p>
     * Note that the specified Message objects <strong>must</strong> belong to
     * this folder. Certain Folder implementations can optimize the operation of
     * setting Flags for a group of messages, so clients might want to use this
     * method, rather than invoking <code>Message.setFlags</code> for each
     * Message.
     * <p>
     * This implementation degenerates to invoking <code>setFlags()</code> on
     * each Message object. Specific Folder implementations that can optimize
     * this case should do so. Also, an implementation must not abort the
     * operation if a Message in the array turns out to be an expunged Message.
     * 
     * @param msgs the array of message objects
     * @param flag Flags object containing the flags to be set
     * @param value set the flags to this boolean value
     * @exception IllegalStateException if this folder is not opened
     *                or if it has been opened READ_ONLY.
     * @exception MessagingException
     * @see Message#setFlags
     * @see javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(Message[] msgs, Flags flag, boolean value)
            throws MessagingException {
        for (int i = 0; i < msgs.length; i++) {
            try {
                msgs[i].setFlags(flag, value);
            } catch (MessageRemovedException me) {
                // This message is expunged, skip
            }
        }
    }

    /**
     * Set the specified flags on the messages numbered from start
     * through end, both start and end inclusive. Note that message
     * numbers start at 1, not 0.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder.
     * <p>
     * Certain Folder implementations can optimize the operation of setting
     * Flags for a group of messages, so clients might want to use this method,
     * rather than invoking <code>Message.setFlags</code> for each Message.
     * <p>
     * The default implementation uses <code>getMessage(int)</code> to get each
     * <code>Message</code> object and then invokes <code>setFlags</code> on
     * that object to set the flags. Specific Folder implementations that can
     * optimize this case should do so. Also, an implementation must not abort
     * the operation if a message number refers to an expunged message.
     * 
     * @param start the number of the first message
     * @param end the number of the last message
     * @param flag Flags object containing the flags to be set
     * @param value set the flags to this boolean value
     * @exception IllegalStateException if this folder is not opened
     *                or if it has been opened READ_ONLY.
     * @exception IndexOutOfBoundsException if the start or end
     *                message numbers are out of range.
     * @exception MessagingException
     * @see Message#setFlags
     * @see javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(int start, int end, Flags flag, boolean value)
            throws MessagingException {
        for (int i = start; i <= end; i++) {
            try {
                Message msg = getMessage(i);
                msg.setFlags(flag, value);
            } catch (MessageRemovedException me) {
                // This message is expunged, skip
            }
        }
    }

    /**
     * Set the specified flags on the messages whose message numbers
     * are in the array.
     * This will result in appropriate MessageChangedEvents being
     * delivered to any MessageChangedListener registered on this
     * Message's containing folder.
     * <p>
     * Certain Folder implementations can optimize the operation of setting
     * Flags for a group of messages, so clients might want to use this method,
     * rather than invoking <code>Message.setFlags</code> for each Message.
     * <p>
     * The default implementation uses <code>getMessage(int)</code> to get each
     * <code>Message</code> object and then invokes <code>setFlags</code> on
     * that object to set the flags. Specific Folder implementations that can
     * optimize this case should do so. Also, an implementation must not abort
     * the operation if a message number refers to an expunged message.
     * 
     * @param msgnums the array of message numbers
     * @param flag Flags object containing the flags to be set
     * @param value set the flags to this boolean value
     * @exception IllegalStateException if this folder is not opened
     *                or if it has been opened READ_ONLY.
     * @exception IndexOutOfBoundsException if any message number
     *                in the given array is out of range.
     * @exception MessagingException
     * @see Message#setFlags
     * @see javax.mail.event.MessageChangedEvent
     */
    public synchronized void setFlags(int[] msgnums, Flags flag, boolean value)
            throws MessagingException {
        for (int i = 0; i < msgnums.length; i++) {
            try {
                Message msg = getMessage(msgnums[i]);
                msg.setFlags(flag, value);
            } catch (MessageRemovedException me) {
                // This message is expunged, skip
            }
        }
    }

    /**
     * Copy the specified Messages from this Folder into another
     * Folder. This operation appends these Messages to the
     * destination Folder. The destination Folder does not have to
     * be opened. An appropriate MessageCountEvent
     * is delivered to any MessageCountListener registered on the
     * destination folder when the messages arrive in the folder.
     * <p>
     * Note that the specified Message objects <strong>must</strong> belong to
     * this folder. Folder implementations might be able to optimize this method
     * by doing server-side copies.
     * <p>
     * This implementation just invokes <code>appendMessages()</code> on the
     * destination folder to append the given Messages. Specific folder
     * implementations that support server-side copies should do so, if the
     * destination folder's Store is the same as this folder's Store. Also, an
     * implementation must not abort the operation if a Message in the array
     * turns out to be an expunged Message.
     * 
     * @param msgs the array of message objects
     * @param folder the folder to copy the messages to
     * @exception FolderNotFoundException if the destination
     *                folder does not exist.
     * @exception IllegalStateException if this folder is not opened.
     * @exception MessagingException
     * @see #appendMessages
     */
    public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
        if (!folder.exists())
            throw new FolderNotFoundException(folder.getFullName() + " does not exist", folder);

        folder.appendMessages(msgs);
    }

    /**
     * Expunge (permanently remove) messages marked DELETED. Returns an
     * array containing the expunged message objects. The
     * <code>getMessageNumber</code> method
     * on each of these message objects returns that Message's original
     * (that is, prior to the expunge) sequence number. A MessageCountEvent
     * containing the expunged messages is delivered to any
     * MessageCountListeners registered on the folder.
     * <p>
     * Expunge causes the renumbering of Message objects subsequent to the
     * expunged messages. Clients that use message numbers as references to
     * messages should be aware of this and should be prepared to deal with the
     * situation (probably by flushing out existing message number caches and
     * reloading them). Because of this complexity, it is better for clients to
     * use Message objects as references to messages, rather than message
     * numbers. Any expunged Messages objects still have to be pruned, but other
     * Messages in that folder are not affected by the expunge.
     * <p>
     * After a message is expunged, only the <code>isExpunged</code> and
     * <code>getMessageNumber</code> methods are still valid on the
     * corresponding Message object; other methods may throw
     * <code>MessageRemovedException</code>
     * 
     * @return array of expunged Message objects
     * @exception FolderNotFoundException if this folder does not
     *                exist
     * @exception IllegalStateException if this folder is not opened.
     * @exception MessagingException
     * @see Message#isExpunged
     * @see javax.mail.event.MessageCountEvent
     */
    public abstract Message[] expunge() throws MessagingException;

    /**
     * Search this Folder for messages matching the specified
     * search criterion. Returns an array containing the matching
     * messages . Returns an empty array if no matches were found.
     * <p>
     * This implementation invokes <code>search(term, getMessages())</code>, to
     * apply the search over all the messages in this folder. Providers that can
     * implement server-side searching might want to override this method to
     * provide a more efficient implementation.
     * 
     * @param term the search criterion
     * @return array of matching messages
     * @exception javax.mail.search.SearchException if the search
     *                term is too complex for the implementation to handle.
     * @exception FolderNotFoundException if this folder does
     *                not exist.
     * @exception IllegalStateException if this folder is not opened.
     * @exception MessagingException
     * @see javax.mail.search.SearchTerm
     */
    public Message[] search(SearchTerm term) throws MessagingException {
        return search(term, getMessages());
    }

    /**
     * Search the given array of messages for those that match the
     * specified search criterion. Returns an array containing the
     * matching messages. Returns an empty array if no matches were
     * found.
     * <p>
     * Note that the specified Message objects <strong>must</strong> belong to
     * this folder.
     * <p>
     * This implementation iterates through the given array of messages, and
     * applies the search criterion on each message by calling its
     * <code>match()</code> method with the given term. The messages that
     * succeed in the match are returned. Providers that can implement
     * server-side searching might want to override this method to provide a
     * more efficient implementation. If the search term is too complex or
     * contains user-defined terms that cannot be executed on the server,
     * providers may elect to either throw a SearchException or degenerate to
     * client-side searching by calling <code>super.search()</code> to invoke
     * this implementation.
     * 
     * @param term the search criterion
     * @param msgs the messages to be searched
     * @return array of matching messages
     * @exception javax.mail.search.SearchException if the search
     *                term is too complex for the implementation to handle.
     * @exception IllegalStateException if this folder is not opened
     * @exception MessagingException
     * @see javax.mail.search.SearchTerm
     */
    public Message[] search(SearchTerm term, Message[] msgs) throws MessagingException {
        Vector matchedMsgs = new Vector();

        // Run thru the given messages
        for (int i = 0; i < msgs.length; i++) {
            try {
                if (msgs[i].match(term)) // matched
                    matchedMsgs.addElement(msgs[i]); // add it
            } catch (MessageRemovedException mrex) {}
        }

        Message[] m = new Message[matchedMsgs.size()];
        matchedMsgs.copyInto(m);
        return m;
    }

    /*
     * The set of listeners are stored in Vectors appropriate to their
     * type. We mark all listener Vectors as "volatile" because, while
     * we initialize them inside this folder's synchronization lock,
     * they are accessed (checked for null) in the "notify" methods,
     * which can't be synchronized due to lock ordering constraints.
     * Since the listener fields (the handles on the Vector objects)
     * are only ever set, and are never cleared, we believe this is
     * safe. The code that dispatches the notifications will either
     * see the null and assume there are no listeners or will see the
     * Vector and will process the listeners. There's an inherent race
     * between adding a listener and notifying the listeners; the lack
     * of synchronization during notification does not make the race
     * condition significantly worse. If one thread is setting a
     * listener at the "same" time an event is being dispatched, the
     * dispatch code might not see the listener right away. The
     * dispatch code doesn't have to worry about the Vector handle
     * being set to null, and thus using an out-of-date set of
     * listeners, because we never set the field to null.
     */

    // Vector of connection listeners.
    private volatile Vector connectionListeners = null;

    /**
     * Add a listener for Connection events on this Folder.
     * <p>
     * The implementation provided here adds this listener to an internal list
     * of ConnectionListeners.
     * 
     * @param l the Listener for Connection events
     * @see javax.mail.event.ConnectionEvent
     */
    public synchronized void addConnectionListener(ConnectionListener l) {
        if (connectionListeners == null)
            connectionListeners = new Vector();
        connectionListeners.addElement(l);
    }

    /**
     * Remove a Connection event listener.
     * <p>
     * The implementation provided here removes this listener from the internal
     * list of ConnectionListeners.
     * 
     * @param l the listener
     * @see #addConnectionListener
     */
    public synchronized void removeConnectionListener(ConnectionListener l) {
        if (connectionListeners != null)
            connectionListeners.removeElement(l);
    }

    /**
     * Notify all ConnectionListeners. Folder implementations are
     * expected to use this method to broadcast connection events.
     * <p>
     * The provided implementation queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to the registered ConnectionListeners. Note that the
     * event dispatching occurs in a separate thread, thus avoiding potential
     * deadlock problems.
     * 
     * @param type the ConnectionEvent type
     * @see javax.mail.event.ConnectionEvent
     */
    protected void notifyConnectionListeners(int type) {
        if (connectionListeners != null) {
            ConnectionEvent e = new ConnectionEvent(this, type);
            queueEvent(e, connectionListeners);
        }

        /*
         * Fix for broken JDK1.1.x Garbage collector :
         * The 'conservative' GC in JDK1.1.x occasionally fails to
         * garbage-collect Threads which are in the wait state.
         * This would result in thread (and consequently memory) leaks.
         * 
         * We attempt to fix this by sending a 'terminator' event
         * to the queue, after we've sent the CLOSED event. The
         * terminator event causes the event-dispatching thread to
         * self destruct.
         */
        if (type == ConnectionEvent.CLOSED)
            terminateQueue();
    }

    // Vector of folder listeners
    private volatile Vector folderListeners = null;

    /**
     * Add a listener for Folder events on this Folder.
     * <p>
     * The implementation provided here adds this listener to an internal list
     * of FolderListeners.
     * 
     * @param l the Listener for Folder events
     * @see javax.mail.event.FolderEvent
     */
    public synchronized void addFolderListener(FolderListener l) {
        if (folderListeners == null)
            folderListeners = new Vector();
        folderListeners.addElement(l);
    }

    /**
     * Remove a Folder event listener.
     * <p>
     * The implementation provided here removes this listener from the internal
     * list of FolderListeners.
     * 
     * @param l the listener
     * @see #addFolderListener
     */
    public synchronized void removeFolderListener(FolderListener l) {
        if (folderListeners != null)
            folderListeners.removeElement(l);
    }

    /**
     * Notify all FolderListeners registered on this Folder and
     * this folder's Store. Folder implementations are expected
     * to use this method to broadcast Folder events.
     * <p>
     * The implementation provided here queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to the FolderListeners registered on this folder. The
     * implementation also invokes <code>notifyFolderListeners</code> on this
     * folder's Store to notify any FolderListeners registered on the store.
     * 
     * @param type type of FolderEvent
     * @see #notifyFolderRenamedListeners
     */
    protected void notifyFolderListeners(int type) {
        if (folderListeners != null) {
            FolderEvent e = new FolderEvent(this, this, type);
            queueEvent(e, folderListeners);
        }
        store.notifyFolderListeners(type, this);
    }

    /**
     * Notify all FolderListeners registered on this Folder and
     * this folder's Store about the renaming of this folder.
     * Folder implementations are expected to use this method to
     * broadcast Folder events indicating the renaming of folders.
     * <p>
     * The implementation provided here queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to the FolderListeners registered on this folder. The
     * implementation also invokes <code>notifyFolderRenamedListeners</code> on
     * this folder's Store to notify any FolderListeners registered on the
     * store.
     * 
     * @param folder Folder representing the new name.
     * @see #notifyFolderListeners
     * @since JavaMail 1.1
     */
    protected void notifyFolderRenamedListeners(Folder folder) {
        if (folderListeners != null) {
            FolderEvent e = new FolderEvent(this, this, folder, FolderEvent.RENAMED);
            queueEvent(e, folderListeners);
        }
        store.notifyFolderRenamedListeners(this, folder);
    }

    // Vector of MessageCount listeners
    private volatile Vector messageCountListeners = null;

    /**
     * Add a listener for MessageCount events on this Folder.
     * <p>
     * The implementation provided here adds this listener to an internal list
     * of MessageCountListeners.
     * 
     * @param l the Listener for MessageCount events
     * @see javax.mail.event.MessageCountEvent
     */
    public synchronized void addMessageCountListener(MessageCountListener l) {
        if (messageCountListeners == null)
            messageCountListeners = new Vector();
        messageCountListeners.addElement(l);
    }

    /**
     * Remove a MessageCount listener.
     * <p>
     * The implementation provided here removes this listener from the internal
     * list of MessageCountListeners.
     * 
     * @param l the listener
     * @see #addMessageCountListener
     */
    public synchronized void removeMessageCountListener(MessageCountListener l) {
        if (messageCountListeners != null)
            messageCountListeners.removeElement(l);
    }

    /**
     * Notify all MessageCountListeners about the addition of messages
     * into this folder. Folder implementations are expected to use this
     * method to broadcast MessageCount events for indicating arrival of
     * new messages.
     * <p>
     * The provided implementation queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to the registered MessageCountListeners. Note that the
     * event dispatching occurs in a separate thread, thus avoiding potential
     * deadlock problems.
     */
    protected void notifyMessageAddedListeners(Message[] msgs) {
        if (messageCountListeners == null)
            return;

        MessageCountEvent e = new MessageCountEvent(this, MessageCountEvent.ADDED, false, msgs);

        queueEvent(e, messageCountListeners);
    }

    /**
     * Notify all MessageCountListeners about the removal of messages
     * from this Folder. Folder implementations are expected to use this
     * method to broadcast MessageCount events indicating removal of
     * messages.
     * <p>
     * The provided implementation queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to the registered MessageCountListeners. Note that the
     * event dispatching occurs in a separate thread, thus avoiding potential
     * deadlock problems.
     */
    protected void notifyMessageRemovedListeners(boolean removed, Message[] msgs) {
        if (messageCountListeners == null)
            return;

        MessageCountEvent e = new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, msgs);
        queueEvent(e, messageCountListeners);
    }

    // Vector of MessageChanged listeners.
    private volatile Vector messageChangedListeners = null;

    /**
     * Add a listener for MessageChanged events on this Folder.
     * <p>
     * The implementation provided here adds this listener to an internal list
     * of MessageChangedListeners.
     * 
     * @param l the Listener for MessageChanged events
     * @see javax.mail.event.MessageChangedEvent
     */
    public synchronized void addMessageChangedListener(MessageChangedListener l) {
        if (messageChangedListeners == null)
            messageChangedListeners = new Vector();
        messageChangedListeners.addElement(l);
    }

    /**
     * Remove a MessageChanged listener.
     * <p>
     * The implementation provided here removes this listener from the internal
     * list of MessageChangedListeners.
     * 
     * @param l the listener
     * @see #addMessageChangedListener
     */
    public synchronized void removeMessageChangedListener(MessageChangedListener l) {
        if (messageChangedListeners != null)
            messageChangedListeners.removeElement(l);
    }

    /**
     * Notify all MessageChangedListeners. Folder implementations are
     * expected to use this method to broadcast MessageChanged events.
     * <p>
     * The provided implementation queues the event into an internal event
     * queue. An event dispatcher thread dequeues events from the queue and
     * dispatches them to registered MessageChangedListeners. Note that the
     * event dispatching occurs in a separate thread, thus avoiding potential
     * deadlock problems.
     */
    protected void notifyMessageChangedListeners(int type, Message msg) {
        if (messageChangedListeners == null)
            return;

        MessageChangedEvent e = new MessageChangedEvent(this, type, msg);
        queueEvent(e, messageChangedListeners);
    }

    /*
     * The queue of events to be delivered.
     */
    private EventQueue q;

    /*
     * A lock for creating the EventQueue object. Only one thread should
     * create an EventQueue for this folder. We can't synchronize on the
     * folder's lock because that would violate the locking hierarchy in
     * some cases. For details, see the IMAP provider.
     */
    private Object qLock = new Object();

    /*
     * Add the event and vector of listeners to the queue to be delivered.
     */
    private void queueEvent(MailEvent event, Vector vector) {
        // synchronize creation of the event queue
        synchronized (qLock) {
            if (q == null)
                q = new EventQueue();
        }

        /*
         * Copy the vector in order to freeze the state of the set
         * of EventListeners the event should be delivered to prior
         * to delivery. This ensures that any changes made to the
         * Vector from a target listener's method during the delivery
         * of this event will not take effect until after the event is
         * delivered.
         */
        Vector v = (Vector) vector.clone();
        q.enqueue(event, v);
    }

    static class TerminatorEvent extends MailEvent {

        private static final long serialVersionUID = 3765761925441296565L;

        TerminatorEvent() {
            super(new Object());
        }

        public void dispatch(Object listener) {
            // Kill the event dispatching thread.
            Thread.currentThread().interrupt();
        }
    }

    // Dispatch the terminator
    private void terminateQueue() {
        synchronized (qLock) {
            if (q != null) {
                Vector dummyListeners = new Vector();
                dummyListeners.setSize(1); // need atleast one listener
                q.enqueue(new TerminatorEvent(), dummyListeners);
                q = null;
            }
        }
    }

    protected void finalize() throws Throwable {
        super.finalize();
        terminateQueue();
    }

    /**
     * override the default toString(), it will return the String
     * from Folder.getFullName() or if that is null, it will use
     * the default toString() behavior.
     */

    public String toString() {
        String s = getFullName();
        if (s != null)
            return s;
        else
            return super.toString();
    }
}
